package mskruch.gps.calculation;

import mskruch.gps.Point;

public class PointsCalculatorImpl implements PointsCalculator {
    private PositionsCalculator positionsCalculator;
    private HeartRateCalculator heartRateCalculator;
    private ElevationCalculator elevationCalculator;
    private CadenceCalculator cadenceCalculator;

    /** most recent added point */
    private Point last = null;
    /** first point */
    private Point first = null;
    
    private double lastElevation;
    private double lastDistance;
    private double elevationGainDistance;
    private double elevationLossDistance;

    PointsCalculatorImpl() {
        positionsCalculator = Calculations.newPositionsCalculator();
        heartRateCalculator = Calculations.newHeartRateCalculator();
        elevationCalculator = Calculations.newElevationCalculator();
        cadenceCalculator = Calculations.newCadenceCalculator();
    }

    @Override
    public PointsCalculator add(Point point) {
        positionsCalculator.add(point);
        heartRateCalculator.add(point);
        elevationCalculator.add(point);
        cadenceCalculator.add(point);
        
        if (first == null) {
            /* first point added */
            first = point;
            lastElevation = elevationCalculator.getCurrent();
            lastDistance = positionsCalculator.getDistance();
        } else {
            /* not first point */
            if (elevationCalculator.getCurrent() != lastElevation) {
                /* elevation changed - measure the distance */
                if (elevationCalculator.getCurrent() > lastElevation) {
                    elevationGainDistance += positionsCalculator.getDistance() - lastDistance;
                } else if (elevationCalculator.getCurrent() < lastElevation) {
                    elevationLossDistance += positionsCalculator.getDistance() - lastDistance;
                }
                lastElevation = elevationCalculator.getCurrent();
                lastDistance = positionsCalculator.getDistance();
            }
        }
        last = point;
        return this;
    }

    @Override
    public PointsCalculator add(Point[] points) {
        for (Point point : points) {
            add(point);
        }
        return this;
    }

    @Override
    public double getDistance() {
        return positionsCalculator.getDistance();
    }

    @Override
    public double getTime() {
        if (last == null || last == first) {
            return 0;
        }
        return ((double) last.getTime() - first.getTime()) / 1000;
    }

    public Short getAverageHeartRate() {
        return heartRateCalculator.getAverage();
    }

    public Short getMaximumHeartRate() {
        return heartRateCalculator.getMaximum();
    }

    public Short getMinimumHeartRate() {
        return heartRateCalculator.getMinimum();
    }

    public Double getCurrentElevation() {
        return elevationCalculator.getCurrent();
    }

    public double getElevationGain() {
        return elevationCalculator.getElevationGain();
    }

    public double getElevationLoss() {
        return elevationCalculator.getElevationLoss();
    }

    public double getElevationGainDistance() {
        if (last == null || last.getElevation() < lastElevation) {
            return elevationGainDistance;
        } else {
            return elevationGainDistance + (positionsCalculator.getDistance() - lastDistance);
        }
    }
    
    public double getElevationLossDistance() {
        if (last == null || last.getElevation() >= lastElevation) {
            return elevationLossDistance;
        } else {
            return elevationLossDistance + (positionsCalculator.getDistance() - lastDistance);
        }    }

    public Short getAverageCadence() {
        return cadenceCalculator.getAverage();
    }

    public Short getMaximumCadence() {
        return cadenceCalculator.getMaximum();
    }

    public Short getMinimumCadence() {
        return cadenceCalculator.getMinimum();
    }

}
